#include "SQLSelectListCache.h"
#include "../../utils/StringUtils.h"
#include "../../utils/FnvHash.h"
#include "../ast/statement/SQLSelectQueryBlock.h"

#include "SQLParserUtils.h"
#include "SQLSelectParser.h"
#include "../../utils/log.h"

SQLSelectListCache::Entry::Entry(string_ptr sql_, SQLSelectQueryBlock *queryBlock_, string_ptr printSql_, long printSqlHash_)
    : sql(sql_),
      queryBlock(queryBlock_),
      printSql(printSql_),
      printSqlHash(printSqlHash_)
{
}

SQLSelectListCache::SQLSelectListCache(DbType_ptr dbType)
{
  this->dbType = dbType;
  // entries->clear();
  entries = std::make_shared<std::list<std::shared_ptr<Entry>>>();
}

void SQLSelectListCache::add(string_ptr select)
{
  LOG_INFO << "str:" << select;

  if (select->empty() || select->length() == 0)
  {
    return;
  }

  SQLSelectParser_ptr selectParser = SQLParserUtils::createSQLStatementParser(select, dbType)->createSQLSelectParser();
  SQLSelectQueryBlock_ptr queryBlock = SQLSelectQueryBlock_ptr(SQLParserUtils::createSelectQueryBlock(dbType));
  selectParser->accept(Token::SELECT);
  selectParser->parseSelectList(queryBlock);
  selectParser->accept(Token::FROM);
  selectParser->accept(Token::XEOF);

  string_ptr printSql = queryBlock->toString();
  long printSqlHash = FnvHash::fnv1a_64_lower(printSql);
  entries->push_back(
      std::shared_ptr<Entry>(std::shared_ptr<SQLSelectListCache::Entry>(new Entry(
          make_string_ptr(select->substr(6)),
          queryBlock.get(),
          printSql,
          printSqlHash))));

  if (entries->size() > 5)
  {
    LOG_INFO << "SelectListCache is too large.";
  }
}

int SQLSelectListCache::getSize()
{
  return entries->size();
}

void SQLSelectListCache::clear()
{
  entries->clear();
}

BOOL_ptr SQLSelectListCache::match(Lexer_ptr lexer, SQLSelectQueryBlock_ptr queryBlock)
{
  if (lexer->token_->name->c_str() != Token::SELECT.name->c_str())
  {
    return BOOL::FALSE;
  }

  int pos_ = lexer->pos_;
  std::wstring text = lexer->text;

  auto it_entries = entries->begin();
  for (int i = 0; i < entries->size(); i++)
  {
    std::advance(it_entries, i);
    std::shared_ptr<Entry> entry = (*it_entries);
    string_ptr block = entry->sql;
    if (StringUtils::startWith(make_string_ptr(encode_util::UnicodeToUTF8(text)), block, pos_))
    {
      // SQLSelectQueryBlock queryBlockCached = queryBlockCache.get(i);
      //  queryBlockCached.cloneSelectListTo(queryBlock);
      queryBlock->setCachedSelectList(entry->printSql, entry->printSqlHash);

      int len = pos_ + block->length();
      // todo fix reset
      lexer->reset(len, text.at(len), Token_ptr(new Token(&Token::FROM)));
      return BOOL::TRUE;
    }
  }
  return BOOL::FALSE;
}
